﻿/**********************************************************/
/* Express- en lopende banden fase */
/* 13-10-2010 - Groep 6: Scheidsrechter(Lars Mohr 0629693, Roy Mengelers 0705020, Roy Lieben 0847186, Timothy Janssen 0951986, Lars Brand 0901164 - */
/*	
/* Omschrijving:
/* In deze klasse wordt het bewegen van de express- en lopende banden geregeld. De nieuwe positie van de spelers wordt bepaald na het bewegen van de banden.*/
/**********************************************************/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Drawing;
using Roborally.CommonData.GlobaleTypes;
using Roborally.CommonData.Enums;

namespace Roborally.Scheidsrechter
{
    internal static class LoopbandenFase
    {
        /// <summary>
        /// Voert de lopende- en expressbanden uit.
        /// </summary>
        /// <exception>InputParameters is null.</exception>
        /// <exception>InputParameters.Bord is null.</exception>
        /// <param name="InputParameters.Bord">Het bord waarop de loopbanden zich bevinden.</param>
        /// <param name="InputParameters.GegevensSpelers">Lijst van alle spelers op het bord.</param>
        /// <returns>Nieuwe status van het bord en de spelers.</returns>
        public static ReturnScheidsrechter LoopBanden(InputScheidsrechter InputParameters)
        {
            if (InputParameters == null) throw new Exception("InputParameters - Null reference.");
            if (InputParameters.Model == null) throw new Exception("InputParameters.Bord - Null reference.");

            try
            {
                ReturnScheidsrechter output = new ReturnScheidsrechter();
                foreach (SpelerGegevens speler in InputParameters.GegevensSpelers)
                    output.GegevensSpelers.Add(speler);
                SortSpelers(output.GegevensSpelers, InputParameters.Model);
                VoerLoopbandUit(1, InputParameters.Model, output.GegevensSpelers);
                SortSpelers(output.GegevensSpelers, InputParameters.Model);
                VoerLoopbandUit(2, InputParameters.Model, output.GegevensSpelers);
                return output;
            }
            catch (Exception e) { throw new Exception("LoopBanden - Uitvoeren van de loopband mislukt.", e); }
        }

        /// <summary>
        /// Sorteerd de lijst met spelers a.d.h.v. de positie in de rij op de loopband. (Spelers in een file)
        /// </summary>
        /// <param name="GegevensSpelers">Lijst met Spelers.</param>
        /// <param name="Bord">Huidige status van het bord.</param>
        private static void SortSpelers(List<SpelerGegevens> GegevensSpelers, IModel Bord)
        {
            for (int i = 0; i < GegevensSpelers.Count; i++)
			{
                for (int j = 0; j < GegevensSpelers.Count; j++)
                {
                    Point nieuwePos = NieuwePositieLoopband(GegevensSpelers[i], Bord);
                    if (nieuwePos.X == GegevensSpelers[j].Positie.X && nieuwePos.Y == GegevensSpelers[j].Positie.Y)
                        Swap(GegevensSpelers, i, j);
                }
			}
        }

        /// <summary>
        /// Wisselt de opgegeven indexen in de lijst.
        /// </summary>
        /// <param name="GegevensSpelers">Lijst met Spelers.</param>
        /// <param name="Index">Index van</param>
        /// <param name="Index2">Index naar</param>
        private static void Swap(List<SpelerGegevens> GegevensSpelers, int Index, int Index2)
        {
            SpelerGegevens tmp = GegevensSpelers[Index];
            GegevensSpelers[Index] = GegevensSpelers[Index2];
            GegevensSpelers[Index2] = tmp;
        }

        /// <summary>
        /// Geeft de nieuwe positie van een speler a.d.h.v. het loopband vakje waar de speler op staat.
        /// </summary>
        /// <param name="Speler">Huidige speler.</param>
        /// <param name="Bord">Huidige status van het bord.</param>
        /// <returns></returns>
        private static Point NieuwePositieLoopband(SpelerGegevens Speler, IModel Bord)
        {
            Point pnt = new Point(-1, -1);
            if (Bewegen.PositieOpBord(Speler.Positie, Bord))
            {
                IVak vak = Bord.Vakken[Speler.Positie.X, Speler.Positie.Y];
                if (vak.KrijgType() == VakType.Expresband || vak.KrijgType() == VakType.Lopendeband)
                {
                    LopendebandAdapter lba = new LopendebandAdapter(vak);
                    pnt = Bewegen.NieuwePositie(Speler.Positie, lba.Richting);
                }
            }
            return pnt;
        }

        /// <summary>
        /// Voert 1 stap op de loopband uit en verplaatst de juiste spelers a.d.h.v. de fase waarin de loopbanden zich bevinden.
        /// </summary>
        /// <param name="Fase">Fase waarin de loopbanden zich bevinden.</param>
        /// <param name="Bord">Huidige status van het bord.</param>
        /// <param name="GegevensSpelers">Alle spelers op het bord.</param>
        private static void VoerLoopbandUit(int Fase, IModel Bord, List<SpelerGegevens> GegevensSpelers)
        {
            for (int i = GegevensSpelers.Count - 1; i >= 0; i--)
            {
                SpelerGegevens speler = GegevensSpelers[i];
                GegevensSpelers.Remove(speler);
                if (Bewegen.PositieOpBord(speler.Positie, Bord))
                {
                    IVak bordVak = Bord.Vakken[speler.Positie.X, speler.Positie.Y];
                    bool zetStap = false;
                    switch (Fase)
                    {
                        case 1:
                            if (bordVak.KrijgType() == VakType.Expresband)
                                zetStap = ZetMogelijkSpeler(Fase, speler, GegevensSpelers, Bord);
                            break;
                        default:
                            if (bordVak.KrijgType() == VakType.Expresband || bordVak.KrijgType() == VakType.Lopendeband)
                                zetStap = ZetMogelijkSpeler(Fase, speler, GegevensSpelers, Bord);
                            break;
                    }

                    if (zetStap)
                    {
                        LopendebandAdapter vak = new LopendebandAdapter(bordVak);
                        Bewegen.ZetStap(GegevensSpelers, speler, vak.Richting, Bord);

                        LopendebandAdapter nieuwVak = new LopendebandAdapter(Bord.Vakken[speler.Positie.X, speler.Positie.Y]);
                        if (nieuwVak.Bocht(vak.Richting) && nieuwVak.Richting != vak.Richting)
                        {
                            if (nieuwVak.Richting == Richting.Zuid && vak.Richting == Richting.Oost)
                                Bewegen.DraaiSpelerOost(speler);
                            else if (nieuwVak.Richting == Richting.Zuid && vak.Richting == Richting.West)
                                Bewegen.DraaiSpelerWest(speler);
                            else if (nieuwVak.Richting == Richting.Noord && vak.Richting == Richting.Oost)
                                Bewegen.DraaiSpelerWest(speler);
                            else if (nieuwVak.Richting == Richting.Noord && vak.Richting == Richting.West)
                                Bewegen.DraaiSpelerOost(speler);
                            else if (nieuwVak.Richting == Richting.West && vak.Richting == Richting.Noord)
                                Bewegen.DraaiSpelerWest(speler);
                            else if (nieuwVak.Richting == Richting.West && vak.Richting == Richting.Zuid)
                                Bewegen.DraaiSpelerOost(speler);
                            else if (nieuwVak.Richting == Richting.Oost && vak.Richting == Richting.Noord)
                                Bewegen.DraaiSpelerOost(speler);
                            else if (nieuwVak.Richting == Richting.Oost && vak.Richting == Richting.Zuid)
                                Bewegen.DraaiSpelerWest(speler);
                        }
                    }
                }
                GegevensSpelers.Add(speler);
            }
        }

        /// <summary>
        /// Controleer of een zet mogelijk is. Hierbij rekening gehouden met:
        /// - Robots samen kunnen komen van 2 verschillende loopbanden. (Deze botsen dan en blijven staan)
        /// - Er staat een tegenstander voor de huidige speler.
        ///     - Zet v/d tegenstander is mogelijk.
        ///     - Deze staat op het einde v/d loopband.
        /// </summary>
        /// <param name="Fase">Fase waarin de loopbanden zich bevinden.</param>
        /// <param name="GegevensEenSpeler">Huidige speler waar de controle voor geld.</param>
        /// <param name="Tegenstanders">Lijst van tegestanders op het bord.</param>
        /// <param name="Bord">Huidige status van het bord.</param>
        /// <returns></returns>
        private static bool ZetMogelijkSpeler(int Fase, SpelerGegevens GegevensEenSpeler, List<SpelerGegevens> GegevensSpelers, IModel Bord)
        {
            bool zetMogelijk = ZetMogelijkBotsen(Fase, GegevensEenSpeler, GegevensSpelers, Bord);
            if (zetMogelijk)
            {
                Point nieuwePos = NieuwePositieLoopband(GegevensEenSpeler, Bord);
                SpelerGegevens tegenstander = null;
                for (int i = 0; i < GegevensSpelers.Count && tegenstander == null; i++)
                {
                    if (nieuwePos.X == GegevensSpelers[i].Positie.X && nieuwePos.Y == GegevensSpelers[i].Positie.Y)
                        tegenstander = GegevensSpelers[i];
                }

                if (tegenstander != null && Bewegen.PositieOpBord(tegenstander.Positie, Bord))
                {
                    GegevensSpelers.Remove(tegenstander);
                    IVak tegVak = Bord.Vakken[tegenstander.Positie.X, tegenstander.Positie.Y];
                    zetMogelijk = (tegVak.KrijgType() == VakType.Lopendeband || tegVak.KrijgType() == VakType.Expresband);
                    zetMogelijk = zetMogelijk && ZetMogelijkSpeler(Fase, tegenstander, GegevensSpelers, Bord);
                    GegevensSpelers.Add(tegenstander);
                }
                else
                    zetMogelijk = true;
            }
            return zetMogelijk;
        }

        /// <summary>
        /// Controleer of een zet mogelijk is. Hierbij rekening gehouden met het feit dat er robots samen kunnen komen van 2 
        /// verschillende loopbanden. (Deze botsen dan en blijven staan)
        /// </summary>
        /// <param name="Fase">Fase waarin de loopbanden zich bevinden.</param>
        /// <param name="GegevensEenSpeler">Huidige speler waar de controle voor geld.</param>
        /// <param name="Tegenstanders">Lijst van tegestanders op het bord.</param>
        /// <param name="Bord">Huidige status van het bord.</param>
        /// <returns>Zet mogelijk? (botsen niet)</returns>
        private static bool ZetMogelijkBotsen(int Fase, SpelerGegevens GegevensEenSpeler, List<SpelerGegevens> Tegenstanders, IModel Bord)
        {
            bool zetMogelijk = true;
            Point nieuwePositie = NieuwePositieLoopband(GegevensEenSpeler, Bord);
            for (int i = 0; i < Tegenstanders.Count && zetMogelijk; i++)
            {
                SpelerGegevens tegenstander = Tegenstanders[i];
                if (Bewegen.PositieOpBord(tegenstander.Positie, Bord))
                {
                    IVak spelerVak = Bord.Vakken[GegevensEenSpeler.Positie.X, GegevensEenSpeler.Positie.Y];
                    IVak bordVak = Bord.Vakken[tegenstander.Positie.X, tegenstander.Positie.Y];
                    if (bordVak.KrijgType() == VakType.Lopendeband || bordVak.KrijgType() == VakType.Expresband)
                    {
                        Point tegNieuwePositie = NieuwePositieLoopband(tegenstander, Bord);
                        if (tegNieuwePositie.X == nieuwePositie.X && tegNieuwePositie.Y == nieuwePositie.Y)
                            zetMogelijk = (Fase == 1 && spelerVak.KrijgType() == VakType.Expresband && bordVak.KrijgType() == VakType.Lopendeband);
                    }
                }
            }
            return zetMogelijk;
        }
    }
}
